﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;

using Google.GData.Client;
using Google.GData.Documents;
using Google.GData.AccessControl;
using Google.GData.Spreadsheets;

using DataSources.Holder;

namespace CodeBrigadeTE
{
    public partial class MainForm : Form
    {
        private ISet<string> allowedEditorTabs = new SortedSet<string>();

        public MainForm()
        {
            InitializeComponent();

            projectsList.listView.SelectedIndexChanged += new EventHandler(projectsList_SelectedIndexChanged); // HACK
            aspectsList.listView.SelectedIndexChanged += new EventHandler(aspectsList_SelectedIndexChanged); // HACK

            allowedEditorTabs.Add(tabPageDebug.Name);

            // autologin from local file
            string cacheFile = Utils.GetUserDataCacheFileName();
            if (System.IO.File.Exists(cacheFile))
            {
                string[] content = System.IO.File.ReadAllLines(cacheFile);
                if (content.Length > 1)
                {
                    string login = content[0]; // TODO: decode login/password
                    string password = content[1]; // TODO: decode login/password
                    IDataHolder projectsSource = new GoogleSpreadsheets(login, password);
                    if (projectsSource.IsAuthOK())
                    {
                        tbLogin.Text = login;
                        tbPassword.Text = password;

                        ConnectToProjects(projectsSource);
                    }
                }
            }
        }

        private void bLoginUser_Click(object sender, EventArgs e)
        {
            string login = tbLogin.Text;
            string password = tbPassword.Text;
            IDataHolder projectsSource = new GoogleSpreadsheets(login, password);
            if (projectsSource.IsAuthOK())
            {
                if (cbRememberUser.Checked)
                {                    
                    System.IO.File.WriteAllLines(
                        Utils.GetUserDataCacheFileName(),
                        new string[] { login, password } // TODO: encode login/password
                    );
                }

                ConnectToProjects(projectsSource);
            }
            else
            {
                MessageBox.Show("Incorrect login/password. Try again.");
            }
        }

        private void bSighOut_Click(object sender, EventArgs e)
        {
            string cacheFile = Utils.GetUserDataCacheFileName();
            if (System.IO.File.Exists(cacheFile))
            {
                // TODO: overwrite file using trash information before delete
                System.IO.File.Delete(cacheFile);
            }
            tbLogin.Text = string.Empty;
            tbPassword.Text = string.Empty;

            DisconnectFromProjects();
        }

        private void DisconnectFromProjects()
        {
            // update sign in panel
            tbLogin.Enabled = true;
            tbPassword.Enabled = true;
            bSignIn.Enabled = true;
            cbRememberUser.Enabled = true;
            bSighOut.Enabled = false;

            // load projects list
            codeTableEditor.DataSource = null;
            aspectsList.DataSource = null;
            projectsList.DataSource = null;

            timer.Enabled = false; // TEST
        }

        private void ConnectToProjects(IDataHolder projectsSource)
        {
            // update sign in panel
            tbLogin.Enabled = false;
            tbPassword.Enabled = false;
            bSignIn.Enabled = false;
            cbRememberUser.Enabled = false;
            bSighOut.Enabled = true;

            // load projects list
            projectsList.DataSource = projectsSource;

            timer.Enabled = true; // TEST
        }

        private void projectsList_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (projectsList.listView.SelectedItems.Count > 0)
            {
                IDataHolder aspectsSource = projectsList.DataSource.GetChildDataSource(
                    projectsList.listView.SelectedItems[0].Tag as string
                ) as IDataHolder;
                aspectsList.DataSource = aspectsSource;

                MainTabs.SelectedTab = tabEdit;
            }
            else
            {
                aspectsList.DataSource = null;
            }
            aspectsSourceNeedRefresh = true;
        }

        private void aspectsList_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (aspectsList.listView.SelectedItems.Count > 0)
            {
                IDataHolder blocksSource = aspectsList.DataSource.GetChildDataSource(
                    aspectsList.listView.SelectedItems[0].Tag as string
                ) as IDataHolder;                    
                codeTableEditor.DataSource = blocksSource;
            }
            else
            {
                codeTableEditor.DataSource = null;
            }
            cellsSourceNeedRefresh = true;

            allowedEditorTabs.Clear();
            allowedEditorTabs.Add(tabPageDebug.Name);
            allowedEditorTabs.Add(tabPageCode.Name);
            tabControl1.SelectedTab = tabPageCode;
        }

        private void tabControl1_Selecting(object sender, TabControlCancelEventArgs e)
        {
            if (allowedEditorTabs.Contains(e.TabPage.Name))
                return;
            e.Cancel = true;
        }

        private void timer_Tick(object sender, EventArgs e)
        {
            if (!backgroundWorker.IsBusy)
            {
                if (aspectsList.DataSource != null)
                {
                    backgroundWorker.RunWorkerAsync(
                        new string[2] { 
                            aspectsList.DataSource.GetOwnerHash(), 
                            codeTableEditor.DataSource == null ? null : codeTableEditor.DataSource.GetOwnerHash() 
                        }
                    );
                }
                else if (info0.Text != "...")
                {
                    Label[] infos = { info0, info1, info2, info3 };
                    for (int i = 0; i < infos.Length; ++i)
                    {
                        infos[i].Text = "...";
                    }
                }
            }
        }

        Hashtable aspectsUpdatedEntries = null;
        Hashtable cellsUpdatedEntries = null;
        bool aspectsSourceNeedRefresh = false;
        bool cellsSourceNeedRefresh = false;
        DataSources.Watcher.IDataWatcher projectsSource = null;
        DataSources.Watcher.IDataWatcher aspectsSource = null;
        DataSources.Watcher.IDataWatcher cellsSource = null;

        private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            string projectHash = (e.Argument as string[])[0];
            string aspectHash = (e.Argument as string[])[1];

            if (projectsSource == null)
            {
                projectsSource = new DataSources.Watcher.GoogleSpreadsheets(tbLogin.Text, tbPassword.Text);
            }

            if (aspectsSource == null || aspectsSource.GetOwnerHash() != projectHash)
            {
                aspectsSource = projectsSource.GetChildDataSource(projectHash);
            }
            else
            {
                if (aspectsSourceNeedRefresh)
                {
                    aspectsSource.ResetLastUpdated();
                }
                aspectsSource.ReloadData();
            }
            aspectsSourceNeedRefresh = false;

            aspectsUpdatedEntries = aspectsSource.GetUpdatedEntries();
            if (aspectsUpdatedEntries.Count > 0)
            {
                if (aspectHash != null)
                {
                    if (cellsSource == null || cellsSource.GetOwnerHash() != aspectHash)
                    {
                        cellsSource = aspectsSource.GetChildDataSource(aspectHash);
                    }
                    else
                    {
                        if (cellsSourceNeedRefresh)
                        {
                            cellsSource.ResetLastUpdated();
                        }
                        else
                        {
                            Hashtable internallyUpdatedCells = null;
                            // TODO: lock thread
                            if (DataSources.Holder.GoogleCells.internallyUpdatedCells.Count > 0)
                            {
                                internallyUpdatedCells = DataSources.Holder.GoogleCells.internallyUpdatedCells.Clone() as Hashtable;
                                DataSources.Holder.GoogleCells.internallyUpdatedCells.Clear();
                            }
                            // TODO: release thread
                            if (internallyUpdatedCells != null)
                            {
                                foreach (DictionaryEntry cell in internallyUpdatedCells)
                                {
                                    cellsSource.RefreshLastUpdated(cell.Key as string, cell.Value as string);
                                }
                            }
                        }
                        cellsSource.ReloadData();
                    }
                    cellsSourceNeedRefresh = false;

                    cellsUpdatedEntries = cellsSource.GetUpdatedEntries();
                    if (cellsUpdatedEntries.Count == 0)
                    {
                        cellsUpdatedEntries = null;
                    }
                }
                else
                {
                    cellsUpdatedEntries = null;
                }
            }
            else
            {
                aspectsUpdatedEntries = null;
                cellsUpdatedEntries = null;
            }
        }

        private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            Label[] infos = { info0, info1, info2, info3 };
            int i = 0;
            if (aspectsUpdatedEntries != null)
            {
                foreach (DictionaryEntry aspect in aspectsUpdatedEntries)
                {
                    string title = ((DataSources.WorksheetInfo)aspect.Value).title;
                    string changes = "changed...";
                    if (
                        codeTableEditor.DataSource != null && 
                        (aspect.Key as string) == codeTableEditor.DataSource.GetOwnerHash()
                    )
                    {
                        if (cellsUpdatedEntries != null)
                        {
                            changes = cellsUpdatedEntries.Count > 1 ? "cells" : "cell";
                            changes = cellsUpdatedEntries.Count.ToString() + " " + changes + " changed";
                        }
                        else
                        {
                            changes = "modified";
                        }
                    }
                    Label info = infos[i++];
                    info.Text = title + " >>> " + changes;
                    info.ForeColor = Color.Red;
                }
            }
            for (int j = i; j < infos.Length; ++j)
            {
                infos[j].Text = "...";
                infos[j].ForeColor = Color.Black;
            }
        }

        private void bApplyCellUpdates_Click(object sender, EventArgs e)
        {
            if (backgroundWorker.IsBusy)
            {
                MessageBox.Show("backgroundWorker IsBusy. Please try again.");
                return;
            }
            
            timer.Enabled = false;
            if (cellsUpdatedEntries != null && codeTableEditor.DataSource != null)
            {
                foreach (DictionaryEntry updatedCell in cellsUpdatedEntries)
                {
                    codeTableEditor.DataSource.Request(updatedCell.Key as string);
                }
                cellsSource.ResetLastUpdated();
                aspectsUpdatedEntries = null;
                cellsUpdatedEntries = null;
            }
            timer.Enabled = true;
        }
    }
}
